/*
 * Copyright (C) Francisco Morero Peyrona. All rights reserved.
 *
 * This software is published under the terms of Open Source
 * License version 1.1, a copy of which has been included with 
 * this distribution in the License.txt file.
 */

package jdbcmanager.swing.table;

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

import javax.swing.*;
import javax.swing.table.*;

import jdbcmanager.app.App;

/**
 * JTable inherited class used as base class for all JDBC Manager table components.
 *
 * @author Francisco Morero Peyrona
 */

public class JxTable extends JTable
{
	protected TablePopupMenu popupMenu      = null;  // Do [popupMenu == null] to disable it
    protected JList          lstRowNumberer = null;
   
    private boolean bRowSelectionMode = true;
    
    // Needed by printData method
    interface TablePrint
    {
        public void print( JxTable table, String sHeader, String sFooter, boolean bPageDialog, boolean bPreview );
    }
   
   //-------------------------------------------------------------------------//

   /**
    * Class constructor
    */
   public JxTable()
   {
      setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
      setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
      setRowSelectionMode( true );
      
      this.popupMenu = new TablePopupMenu();
      
      addMouseListener( new MouseAdapter()
		{
			public void mouseClicked( MouseEvent evt )
			{
				if( JxTable.this.popupMenu != null && SwingUtilities.isRightMouseButton( evt ) )
					JxTable.this.popupMenu.show( evt );
			}
		} );
   }

   /**
    * Changes teh width (in characters) of a column. 
    * <p>
    * An estimation of number of pixels is done, based on the number of
    * characters and is applied to the column by invoking <code>setPreferredWidth(...)</code>.
    * <p>
    * If the length of the title is bigger than passed argunment, the title length will be used.
    * 
    * @param nCol Column index
    * @param nCharsLen Length in number of characters
    */
   public void setColumnWidth( int nCol, int nCharsLen )
   {
      TableColumn column = getColumnModel().getColumn( nCol );
      String      sTitle = column.getHeaderValue().toString();
      FontMetrics metric = getFontMetrics( getFont() );

      String sTest    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
      int    nAverage = metric.stringWidth( sTest ) / sTest.length();

      nCharsLen = Math.max( nCharsLen, sTitle.length() );   // Gets the bigger of [Title len] and [Field len]
      nCharsLen = Math.max( nCharsLen,   4 );               // Minimum size of 4 chars
      nCharsLen = Math.min( nCharsLen, 128 );               // Column max allowed length is 128 chars

      column.setPreferredWidth( nCharsLen * nAverage );
   }

   /**
    * Increments or decrements table font size
    *
    * @param nIncrement Amount to increment or decrement
    */
   public void setFontSize( int nIncrement )
   {
      Font fntTable  = getFont();
      Font fntHeader = getTableHeader().getFont();

      int nTable   = fntTable.getSize()  + nIncrement;
      int nHeader  = fntHeader.getSize() + nIncrement;

      setRowHeight( nTable + (getIntercellSpacing().height * 2) );   // * 2 -> Up & Down
      setFont( fntTable.deriveFont( (float) nTable ) );
      getTableHeader().setFont( fntHeader.deriveFont( (float) nHeader ) );
      
      if( this.lstRowNumberer != null )
      {
         this.lstRowNumberer.setFixedCellHeight( getRowHeight() );
         ((RowCounterRenderer) this.lstRowNumberer.getCellRenderer()).updateFontSize();
      }
   }

   /**
    * Row (<code>true</code> passed) or Cell (<code>false</code> pased) selection mode
    * 
    * @param b Mode to set.
    */
   public void setRowSelectionMode( boolean b )
   {
      this.bRowSelectionMode = b;
   }

   public boolean isRowSelectionMode()
   {
      return this.bRowSelectionMode;
   }

   public boolean isCellSelected( int nRow, int nColumn )
   {
      if( this.bRowSelectionMode )
         return isRowSelected( nRow );
      
      return isRowSelected( nRow ) && isColumnSelected( nColumn );
   }
   
   /**
    * Prints head and data.
    */
   public void printData( String sHeader, String sFooter, boolean bPageDialog, boolean bPreview )
   {
       String sJavaVer = System.getProperty( "java.version" );
       
       if( sJavaVer.charAt( 0 ) == '1' && sJavaVer.charAt( 2 ) < '5' )
       {
           App.showMessage( "Warning", "You need JRE 1.5.0 or above to be able to print tables." );
       }
       else
       {   
           try
            {
                Class      classDefinition = Class.forName( "jdbcmanager.swing.table.JxTablePrint" );
                TablePrint instance        = (TablePrint) classDefinition.newInstance();
                           instance.print( this, sHeader, sFooter, bPageDialog, bPreview );
            }
            catch( Exception exc )
            {
                App.showException( exc );
            }
       }
   }
   
   /**
    * Prints the results to a Plain text File.
    * @param fileName 
    * @param separator a char what define the separator between fields.
    */
   public void exportDataToPlain(String fileName, char separator) {
    
   		File f = new File(fileName);
   		PrintWriter out = null;
   		try {
   			out = new PrintWriter(new FileOutputStream(f));
		} catch (FileNotFoundException e) {
			jdbcmanager.app.App.showException(e, "Error while creating File");
			return;
		}
   	
   		TableModel tableModel = getModel(); 
		int cols = tableModel.getColumnCount();
		for (int k = 0; k < cols; k++) {
			out.print(tableModel.getColumnName(k));
			if (k < cols - 1) out.print(separator);
		}
		out.println();
	
		int cant = tableModel.getRowCount();
		for (int i = 0; i < cant; i++) {
			for (int k = 0; k < cols; k++) {
				out.print(tableModel.getValueAt(i, k));
				if (k < cols - 1) out.print(separator);
			}
			out.println();
		}
		out.flush();
		out.close();
		jdbcmanager.app.App.showMessage( "Export table", "Export complete!" );
   } 

   
   
   public JList getRowNumberer()
   {
      if( this.lstRowNumberer == null )
      {
         ListModel lm = new AbstractListModel()
         {
            public int    getSize()                 { return getModel().getRowCount(); }
            public Object getElementAt( int index ) { return null; }
         };
            
         this.lstRowNumberer = new JList( lm );
         this.lstRowNumberer.setFixedCellWidth( 35 );
         this.lstRowNumberer.setFixedCellHeight( getRowHeight() );
         this.lstRowNumberer.setCellRenderer( new RowCounterRenderer() );
      }

      return this.lstRowNumberer;
   }
   
   //-------------------------------------------------------------------------//
   
   protected void copyCell( int nRow, int nCol )
   {
       String sCell = getValueAt( nRow, nCol ).toString();
       StringSelection data = new StringSelection( sCell );

       Toolkit.getDefaultToolkit().getSystemClipboard().setContents( data, data );
   }
   
   protected void copyRow( int nRow )
   {
       StringBuffer sbRow = new StringBuffer( 1024 );
       int          nCols = getColumnCount();
       
       for( int n = 0; n < nCols; n++ )
           sbRow.append( '"' ).append( getValueAt( nRow, n ).toString() ).append( "\", " );

       sbRow.setLength( sbRow.length() - 2 );   // Deletes last ", "
       
       StringSelection data = new StringSelection( sbRow.toString() );
       Toolkit.getDefaultToolkit().getSystemClipboard().setContents( data, data );
   }
   
   protected void copyAll()
   {
       StringBuffer sbTable = new StringBuffer( 1024 );
       int          nRows   = getRowCount();
       int          nCols   = getColumnCount();
       
       for( int nRow = 0; nRow < nRows; nRow++ )
       {
           for( int nCol = 0; nCol < nCols; nCol++ )
               sbTable.append( '"' ).append( getValueAt( nRow, nCol ).toString() ).append( "\", " );
           
           sbTable.append( '\n' );
       }

       sbTable.setLength( sbTable.length() - 3 );   // Deletes last ", \n"
       
       StringSelection data = new StringSelection( sbTable.toString() );
       Toolkit.getDefaultToolkit().getSystemClipboard().setContents( data, data );
   }
   
   //-------------------------------------------------------------------------//
   // INNER CLASS: The popup menu used by table
   //-------------------------------------------------------------------------//
    protected class TablePopupMenu extends JPopupMenu
	{
    	///JMenu mnuCols = new JMenu( "Columns" );
    	
    	private JMenuItem itmCopyCell;
    	private JMenuItem itmCopyRow;
    	private JMenuItem itmCopyAll;
    	private JMenuItem itmPrint;
    	private JMenuItem itmExport;
    	
    	private int nRow, nCol;
    	
		public TablePopupMenu()
		{
			this.itmCopyCell = new JMenuItem( "Copy cell", new ImageIcon( getClass().getResource( "images/copy_cell.gif" ) ) );
			this.itmCopyCell.addActionListener( new ActionListener()
				{
					public void actionPerformed( ActionEvent evt )	{ JxTable.this.copyCell( nRow, nCol ); }
				} );
			this.itmCopyRow  = new JMenuItem( "Copy row" , new ImageIcon( getClass().getResource( "images/copy_row.gif"  ) ) );
			this.itmCopyRow.addActionListener( new ActionListener()
				{
					public void actionPerformed( ActionEvent evt )	{ JxTable.this.copyRow( nRow ); }
				} );
			this.itmCopyAll  = new JMenuItem( "Copy all" , new ImageIcon( getClass().getResource( "images/copy_all.gif"  ) ) );
			this.itmCopyAll.addActionListener( new ActionListener()
				{
					public void actionPerformed( ActionEvent evt )	{ JxTable.this.copyAll(); }
				} );
			
			JMenu mnuCopy = new JMenu( "Copy" );
			      mnuCopy.add( this.itmCopyCell );
			      mnuCopy.add( this.itmCopyRow  );
			      mnuCopy.add( this.itmCopyAll  );
			
			this.itmPrint = new JMenuItem( "Print...", new ImageIcon( getClass().getResource( "images/print.gif" ) ) );
			this.itmPrint.addActionListener( new ActionListener()
				{
					public void actionPerformed( ActionEvent evt )	{ JxTable.this.printData( "", "", true, true ); }
				} );
			this.itmExport = new JMenuItem( "Export...", new ImageIcon( getClass().getResource( "images/save.gif" ) ) );
//			this.itmExport.addActionListener(this); //The action Listener is defined in class DbTable

			
			
			// Adding top-level items to menu
			add( mnuCopy );
			///add( this.mnuCols );
			addSeparator();
			add( this.itmPrint );
			add( this.itmExport );
		}
        
        public JMenuItem getItem( String sText )
        {
            sText = sText.toUpperCase();
            
            if(      sText.indexOf( "COPY CELL" ) > -1 ) return this.itmCopyCell;
            else if( sText.indexOf( "COPY ROW"  ) > -1 ) return this.itmCopyRow;
            else if( sText.indexOf( "COPY ALL"  ) > -1 ) return this.itmCopyAll;
            else if( sText.indexOf( "PRINT"     ) > -1 ) return this.itmPrint;
            else if( sText.indexOf( "EXPORT"     ) > -1 ) return this.itmExport;
            ///else if( sText.indexOf( "COLUMNS" ) > -1 ) return this.itmColumns;
            else 
                return null;
        }
		
		/**
		 * Redefined to use selected row and col
		 */
        public void show( MouseEvent evt )
		{
			this.nRow = JxTable.this.rowAtPoint( evt.getPoint() );
			this.nCol = JxTable.this.columnAtPoint( evt.getPoint() );
			
			if( nRow >= 0 && nCol >= 0 )
			{
				JxTable.this.getSelectionModel().setSelectionInterval( nRow, nRow );
				show( evt.getComponent(), evt.getX(), evt.getY() );
			}
			
			///addColumns();
		}
		/* TODO Hacer q las columnas sean visibles o invisibles: mirar en esta clase las l�neas con ///
		public void addColumns()
		{
			int nCols = JxTable.this.getModel().getColumnCount();
			
			this.mnuCols.removeAll();
			
			for( int n = 0; n < nCols; n++ )
			{
				TableColumn       col = JxTable.this.getColumnModel().getColumn( n );
				JCheckBoxMenuItem itm = new JCheckBoxMenuItem( col.getHeaderValue().toString(), true );
				                  itm.addActionListener( new ActionListener()
				        		      {
				                  		  public void actionPerformed( ActionEvent evt )	
				                  		  {
				                  		      JCheckBoxMenuItem item = (JCheckBoxMenuItem) evt.getSource();
				                  		      changeColumnVisibility( item.getText(), item.isSelected() );
				                  		  }
				        		      } );
				
				this.mnuCols.add( itm );
			}
		}
		
		private void changeColumnVisibility( String sColHeader, boolean bVisible )
		{
			System.out.println( sColHeader +" "+ bVisible );
		}*/

	}
   
    //-------------------------------------------------------------------------//
	// INNER CLASS 
	//-------------------------------------------------------------------------//
	private final class RowCounterRenderer extends JLabel implements ListCellRenderer
	{
		private RowCounterRenderer()
		{
			JTableHeader header = getTableHeader();

			setOpaque( true );
			setBorder( UIManager.getBorder( "TableHeader.cellBorder" ) );
			setHorizontalAlignment( CENTER );
			setForeground( header.getForeground() );
			setBackground( header.getBackground() );
			setFont( header.getFont() );
		}

		public Component getListCellRendererComponent( JList list, Object oValue, int nIndex, boolean bSelected,
				boolean bCellHasFocus )
		{
			setText( String.valueOf( nIndex + 1 ) );
			return this;
		}

		private void updateFontSize()
		{
			setFont( getTableHeader().getFont() );
		}
	}
}